/**
 * 统计指定仓库，指定某天代码量数据
 * 注意：
 *  1、自动同步最新仓库
 *  2、统计数据已排除忽略规则
 */
const { getDatespanStr } = require("../utils/DateUtil");
const md5 = require("md5");
const { queryMysql } = require("../utils/queryMysqlUtil");
const {
  createOutputDir,
  cloneOrPull,
  execAsync,
} = require("../utils/scriptUtil");
const { getFilterRulerByRepoId } = require("../model/filterRuler");

/**
 * 统计全部仓库列表在指定日期的代码量数据
 * @param repoInfoList 仓库列表
 * @param date 开始时间(格式：年月日 时分秒)
 * @param output 统计仓库所在目录（若本地不存在则自动创建，并同步最新仓库）
 *
 * 注意：
 *   1、若本地不存在output目录，则自动创建
 *   2、统计前，自动同步最新仓库数据。统计后，自动入库数据（create_time为`Date`）
 */
async function countRepoSpecDate(repoInfoList, date, output) {
  let dateArr = date.split(" ");
  await countRepoSpecDateSpan(
    repoInfoList,
    dateArr[0],
    dateArr[0],
    dateArr[1],
    output
  );
}

/**
 * 统计全部仓库列表在指定起始日期的代码量数据
 *
 * @param {Record<string,any>} repoInfoList 仓库列表
 * @param {string} startNyrDateStr 开始时间(格式：年月日)
 * @param {string} endtNyrDateStr 结束时间(格式：年月日)
 * @param {string} insertTime 格式：时分秒。入库时create_time='年月日 insertTime'
 * @param {string} output 统计仓库所在目录（若本地不存在则自动创建，并同步最新仓库）
 *
 * 注意：
 *   1、若本地不存在output目录，则自动创建
 *   2、统计前，自动同步最新仓库数据。统计后，自动入库起始日期区间的数据（create_time为`当天 12:00:00`）
 */
async function countRepoSpecDateSpan(
  repoInfoList,
  startNyrDateStr,
  endtNyrDateStr,
  insertTime,
  output
) {
  //nyrDate格式：年月日
  //判断output目录是否存在，不存在创建
  createOutputDir(output);

  let taskList = [];

  //删除数据库数据
  await deleteCountData(startNyrDateStr, endtNyrDateStr);

  //同步最新仓库（不存在仓库则clone，存在则pull），统计代码然后入库
  for (let repoInfo of repoInfoList) {
    taskList.push(
      countSingleRepoSpecDateSpan(
        repoInfo,
        startNyrDateStr,
        endtNyrDateStr,
        insertTime,
        output
      )
    );
  }

  let successCount = 0;
  let taskRes = await Promise.allSettled(taskList);
  for (let taskItem of taskRes) {
    if (taskItem.status === "fulfilled") {
      successCount++;
    }
  }
  console.log(
    `共计${taskList.length}个仓库，统计成功${successCount}个，失败${
      taskList.length - successCount
    }个`
  );
}

/**
 * 处理单一仓库在起始时间内，每一天的数据并入库
 * @param {Record<string,any>} repoInfo 单仓库信息
 * @param {string} startNyrDateStr 开始时间(格式：年月日)
 * @param {string} endtNyrDateStr  结束时间(格式：年月日)
 * @param {string} insertTime 格式：时分秒。入库时create_time='年月日 insertTime'
 * @param {string }output 同步的仓库存放目录
 */
async function countSingleRepoSpecDateSpan(
  repoInfo,
  startNyrDateStr,
  endtNyrDateStr,
  insertTime,
  output
) {
  await cloneOrPull(repoInfo, output);

  //获取过滤规则
  let groupFilterRuler = await getFilterRulerByRepoId(repoInfo.rid);
  let filterRuleArr = groupFilterRuler.map((item) => {
    return `':(exclude,glob)${item["filter_ruler"]}'`;
  });
  let filterStr = filterRuleArr.join(" ");

  //获取时间范围
  let nyrDataSpan = getDatespanStr(startNyrDateStr, endtNyrDateStr);

  //将时间范围内的统计数据入库
  for (let nyrdataStr of nyrDataSpan) {
    try {
      //生成统计脚本
      let cmd = `git log --all --after="${nyrdataStr} 00:00:00" --before=="${nyrdataStr} 23:59:59" --pretty=tformat: --numstat ${filterStr} | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "%s,%s,%s", add, subs, loc }'`;
      const stdout = await execAsync(
        cmd,
        `${output}/vcs-${md5(repoInfo.gitlab_clone_url)}`
      );

      //更新代码统计库表（注意：一天如果出现多次入库，则同一个数据库一天可能存在多条统计数据）
      await insertCountData(
        stdout,
        repoInfo.rid,
        `${nyrdataStr} ${insertTime}`
      );
      console.log(
        `仓库：${repoInfo.gitlab_name}，${nyrdataStr}统计数据：'${stdout}' 已入库成功 `
      );
    } catch (err) {
      console.log(
        `仓库：${repoInfo.gitlab_name}，${nyrdataStr}统计数据：'${stdout}' 已入库失败 `,
        err
      );
    }
  }
}

/**
 * 插入代码量统计数据
 * @param countStr 统计数据。格式： '新增,删除,总变化'
 * @param repoRid 统计目标仓库的id
 * @param date 统计的日期。格式：年月日 时分秒
 * @returns {null}
 */
const dayjs = require("dayjs");
async function insertCountData(countStr, repoRid, date) {
  //代码统计数据
  let countArr = countStr.split(",");
  let addLineNum = countArr[0] || 0;
  let removeLineNum = countArr[1] || 0;
  // let totalLineNum = countArr[2] || 0;

  //增加特殊逻辑
  if (parseInt(addLineNum) > 3000) {
    addLineNum = 3000
  }
  if (parseInt(removeLineNum) > 3000) {
    removeLineNum=3000
  }

  totalLineNum=addLineNum-removeLineNum


  const formatDate = dayjs(date).format("YYYY-MM-DD HH:mm:ss");

  //插入数据
  const insertStr = `
        insert into chihiro_code_count(repo_rid,add_num,remove_num,total_num,create_time,update_time) 
        values(${repoRid},${addLineNum},${removeLineNum},${totalLineNum},'${formatDate}','${formatDate}')`;

  await queryMysql(insertStr).catch((err) => {
    return Promise.reject(`统计数据入库失败,${err}`);
  });
  return Promise.resolve();
}

/**
 * 删除指定日期统计数据
 * @param startNyrDateStr 格式：年月日
 * @param endNyrDateStr 格式：年月日
 * @return {Promise<void>}
 */
async function deleteCountData(startNyrDateStr, endNyrDateStr) {
  //数据库如果有data当天的数据，先删除
  const deletStr = `
    delete 
    from chihiro_code_count 
    where create_time between '${startNyrDateStr} 00:00:00' and '${endNyrDateStr} 23:59:59'
  `;
  await queryMysql(deletStr).catch((err) => {
    console.log(
      `${startNyrDateStr}-${endNyrDateStr} 代码统计数据清除失败 --> `,
      err
    );
    return Promise.reject(
      `${startNyrDateStr}-${endNyrDateStr},统计数据清除失败,${err}`
    );
  });

  return Promise.resolve();
}

module.exports = {
  countRepoSpecDate,
  countRepoSpecDateSpan,
};
